// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package parser

import (
	"log"
)

var universe = &Scope{
	Tag:   UniverseScope,
	Names: map[string]*Sym{},
}

func init() {
	for k, v := range universe.Names {
		v.Name = []byte(k)
	}
}

type ScopeTag int

// Values of ScopeTag
const (
	_ ScopeTag = iota
	UniverseScope
	PackageScope
	FileScope
	OtherScope
	opaqueScope    // internal use only
	_ScopeTagLimit // keep last
)

var ScopeTags = map[ScopeTag]string{
	UniverseScope: "UniverseScope",
	PackageScope:  "PackageScope",
	FileScope:     "FileScope",
	OtherScope:    "OtherScope",
	opaqueScope:   "opaqueScope",
}

type Scope struct {
	Outer *Scope
	Tag   ScopeTag
	Eff   *Scope // Most scopes: eff == self, some: eff == outer/parent scope
	Names map[string]*Sym
}

func (s *Scope) dump() {
	log.Printf("Scope@%p, .Eff%p:\n", s, s.Eff)
	for name, sym := range s.Eff.Names {
		log.Printf("[%s]: %s.%s[%s]\n", name, sym.Packname, sym.Name, SymTags[sym.Tag])
	}
}

func (s *Scope) new(tag ScopeTag) *Scope {
	x := &Scope{Outer: s, Tag: tag}
	if s != nil && s.Tag == opaqueScope {
		x.Eff = s
		return x
	}

	x.Names, x.Eff = map[string]*Sym{}, x
	return x
}

func (s *Scope) insert(name string, sym *Sym) (alt *Sym) {
	var ok bool
	if alt, ok = s.Eff.Names[name]; ok {
		return
	}

	s.Eff.Names[name] = sym
	return
}

func (s *Scope) lookup(name string) *Sym {
	return s.Eff.Names[name]
}

func (s *Scope) Depth() (d int) {
	d = -1
	for s != nil {
		d++
		if eff := s.Eff; eff != s {
			s = eff
		}
		if s != nil {
			s = s.Outer
		}
	}
	return
}
